from flask import Blueprint, render_template, jsonify, request, make_response
from neo4j import GraphDatabase
import os
from io import BytesIO
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont

from . import knowledge_graph_bp

# ===== 配置 =====
URI = "bolt://localhost:7687"
AUTH = ("neo4j", "neo4jNEO4J")  # 改成自己的
driver = GraphDatabase.driver(URI, auth=AUTH)

# 统一执行 Cypher 的小工具
def run_cypher(query, params=None):
    with driver.session() as session:
        result = session.run(query, params or {})
        return [r.data() for r in result]

# 图数据：支持筛选
def get_graph_data(filters=None):
    filters = filters or {}
    class_id = filters.get("class_id")
    student_kw = filters.get("student")
    date_val = filters.get("date")
    behavior_types = filters.get("behavior_types")  # list or None
    teacher = filters.get("teacher")
    class_name = filters.get("class_name")
 
    query = """
    MATCH (s:Student)-[r:PERFORMED]->(b:Behavior)-[:OCCURRED_IN]->(c:ClassSession)
    WHERE ($class_id IS NULL OR c.id = $class_id)
      AND ($student_kw IS NULL OR toLower(s.name) CONTAINS toLower($student_kw))
      AND ($date_val IS NULL OR c.date = $date_val)
      AND ($behavior_types IS NULL OR b.type IN $behavior_types)
      AND ($teacher IS NULL OR c.teacher = $teacher)
      AND ($class_name IS NULL OR s.className = $class_name)
    RETURN s.name AS student, b.type AS behavior, b.timestamp AS ts, c.courseName AS course
    """
    params = {
        "class_id": class_id,
        "student_kw": student_kw,
        "date_val": date_val,
        "behavior_types": behavior_types,
        "teacher": teacher,
        "class_name": class_name,
    }
    rows = run_cypher(query, params)

    nodes, edges = {}, []
    for rec in rows:
        st, bh, course = rec.get("student"), rec.get("behavior"), rec.get("course")
        ts_raw = rec.get("ts")
        ts = str(ts_raw).split("T")[1][:5] if ts_raw else ""
        nodes[course] = {"id": course, "label": course, "group": 0}
        nodes[bh] = {"id": bh, "label": bh, "group": 1}
        nodes[st] = {"id": st, "label": st, "group": 2}
        edges.append({"from": st, "to": bh, "label": ts})
        edges.append({"from": bh, "to": course, "label": ""})

    return {"nodes": list(nodes.values()), "edges": edges}

# 提供筛选项（下拉/复选）
def get_filter_options():
    classes = run_cypher(
        """
        MATCH (c:ClassSession)
        RETURN DISTINCT c.id AS id, c.courseName AS name, c.date AS date
        ORDER BY id
        """
    )
    students = run_cypher(
        """
        MATCH (s:Student)
        RETURN DISTINCT s.name AS name, s.className AS className
        ORDER BY name
        """
    )
    behaviors = run_cypher(
        """
        MATCH (b:Behavior)
        RETURN DISTINCT b.type AS type
        ORDER BY type
        """
    )
    # 修复 Neo4j 5+ 语法: 属性存在判断使用 IS NOT NULL
    teachers = run_cypher(
        """
        MATCH (c:ClassSession)
        WHERE c.teacher IS NOT NULL
        RETURN DISTINCT c.teacher AS teacher
        ORDER BY teacher
        """
    )
    class_names = run_cypher(
        """
        MATCH (s:Student)
        WHERE s.className IS NOT NULL
        RETURN DISTINCT s.className AS className
        ORDER BY className
        """
    )
    return {"classes": classes, "students": students, "behaviors": behaviors, "teachers": teachers, "class_names": class_names}

# 统计数据用于 PDF
def get_stats(filters=None):
    filters = filters or {}
    class_id = filters.get("class_id")
    student_kw = filters.get("student")
    date_val = filters.get("date")
    behavior_types = filters.get("behavior_types")
    teacher = filters.get("teacher")
    class_name = filters.get("class_name")
 
    base_where = (
        "($class_id IS NULL OR c.id = $class_id) AND "
        "($student_kw IS NULL OR toLower(s.name) CONTAINS toLower($student_kw)) AND "
        "($date_val IS NULL OR c.date = $date_val) AND "
        "($behavior_types IS NULL OR b.type IN $behavior_types) AND "
        "($teacher IS NULL OR c.teacher = $teacher) AND "
        "($class_name IS NULL OR s.className = $class_name)"
    )
    params = {
        "class_id": class_id,
        "student_kw": student_kw,
        "date_val": date_val,
        "behavior_types": behavior_types,
        "teacher": teacher,
        "class_name": class_name,
    }

    # 行为类型统计
    by_type = run_cypher(
        f"""
        MATCH (s:Student)-[:PERFORMED]->(b:Behavior)-[:OCCURRED_IN]->(c:ClassSession)
        WHERE {base_where}
        RETURN b.type AS type, count(*) AS cnt
        ORDER BY cnt DESC
        """,
        params,
    )

    # 学生参与统计
    by_student = run_cypher(
        f"""
        MATCH (s:Student)-[:PERFORMED]->(b:Behavior)-[:OCCURRED_IN]->(c:ClassSession)
        WHERE {base_where}
        RETURN s.name AS student, count(*) AS cnt
        ORDER BY cnt DESC
        """,
        params,
    )

    # 参与人数
    participants = run_cypher(
        f"""
        MATCH (s:Student)-[:PERFORMED]->(b:Behavior)-[:OCCURRED_IN]->(c:ClassSession)
        WHERE {base_where}
        RETURN count(DISTINCT s) AS participants
        """,
        params,
    )
    participants = participants[0]["participants"] if participants else 0

    # 举手比例（近似教师互动率）
    raise_cnt = run_cypher(
        f"""
        MATCH (s:Student)-[:PERFORMED]->(b:Behavior)-[:OCCURRED_IN]->(c:ClassSession)
        WHERE {base_where} AND toLower(b.type) CONTAINS '举手'
        RETURN count(*) AS cnt
        """,
        params,
    )
    raise_cnt = raise_cnt[0]["cnt"] if raise_cnt else 0

    total_behaviors = run_cypher(
        f"""
        MATCH (s:Student)-[:PERFORMED]->(b:Behavior)-[:OCCURRED_IN]->(c:ClassSession)
        WHERE {base_where}
        RETURN count(*) AS cnt
        """,
        params,
    )
    total_behaviors = total_behaviors[0]["cnt"] if total_behaviors else 0

    interaction_rate = (raise_cnt / total_behaviors) if total_behaviors else 0.0

    return {
        "by_type": by_type,
        "by_student": by_student,
        "participants": participants,
        "total_behaviors": total_behaviors,
        "interaction_rate": interaction_rate,
    }

@knowledge_graph_bp.route('/')
def knowledge_graph():
    return render_template('knowledge_graph.html')

@knowledge_graph_bp.route('/filters')
def filters():
    return jsonify(get_filter_options())

@knowledge_graph_bp.route('/data')
def data():
    # 接收筛选参数
    class_id = request.args.get("class_id", type=int)
    student_kw = request.args.get("student", type=str)
    date_val = request.args.get("date", type=str)
    behavior_types = request.args.getlist("behavior_types") or None
    teacher = request.args.get("teacher", type=str)
    class_name = request.args.get("class_name", type=str)

    filters = {
        "class_id": class_id,
        "student": student_kw,
        "date": date_val,
        "behavior_types": behavior_types,
        "teacher": teacher,
        "class_name": class_name,
    }
    return jsonify(get_graph_data(filters))

@knowledge_graph_bp.route('/report_pdf')
def report_pdf():
    # 生成 PDF 报告
    class_id = request.args.get("class_id", type=int)
    student_kw = request.args.get("student", type=str)
    date_val = request.args.get("date", type=str)
    behavior_types = request.args.getlist("behavior_types") or None
    teacher = request.args.get("teacher", type=str)
    class_name = request.args.get("class_name", type=str)
    filters = {
        "class_id": class_id,
        "student": student_kw,
        "date": date_val,
        "behavior_types": behavior_types,
        "teacher": teacher,
        "class_name": class_name,
    }

    stats = get_stats(filters)

    # 动态生成 PDF（二进制流）
    try:
        buf = BytesIO()
        c = canvas.Canvas(buf, pagesize=A4)
        width, height = A4

        # 注册中文字体：优先 SimHei(TTF)；否则使用内置 CID 字体 STSong-Light；最后回退 Helvetica
        font_name = "SimHei"
        font_path = r"C:\\Windows\\Fonts\\simhei.ttf"
        try:
            if os.path.exists(font_path):
                pdfmetrics.registerFont(TTFont(font_name, font_path))
            else:
                # 使用内置 CJK 字体（无需字体文件）
                pdfmetrics.registerFont(UnicodeCIDFont("STSong-Light"))
                font_name = "STSong-Light"
        except Exception:
            try:
                pdfmetrics.registerFont(UnicodeCIDFont("STSong-Light"))
                font_name = "STSong-Light"
            except Exception:
                font_name = "Helvetica"

        y = height - 30 * mm
        c.setFont(font_name, 16)
        c.drawString(20 * mm, y, "课堂行为报告")
        y -= 12 * mm

        c.setFont(font_name, 11)
        filters_line = f"ClassID: {class_id or '-'}  学生关键词: {student_kw or '-'}  日期: {date_val or '-'}  老师: {teacher or '-'}  班级: {class_name or '-'}"
        c.drawString(20 * mm, y, filters_line)
        y -= 10 * mm

        c.drawString(20 * mm, y, f"参与人数: {stats['participants']}  总行为数: {stats['total_behaviors']}  互动率(举手/总): {stats['interaction_rate']:.2%}")
        y -= 12 * mm

        c.setFont("Helvetica-Bold", 12)
        c.drawString(20 * mm, y, "行为类型统计")
        y -= 8 * mm
        c.setFont("Helvetica", 11)
        for row in stats["by_type"][:15]:
            c.drawString(22 * mm, y, f"{row['type']}: {row['cnt']}")
            y -= 7 * mm
            if y < 20 * mm:
                c.showPage(); y = height - 20 * mm
                c.setFont("Helvetica", 11)

        y -= 6 * mm
        c.setFont("Helvetica-Bold", 12)
        c.drawString(20 * mm, y, "学生参与统计(Top 20)")
        y -= 8 * mm
        c.setFont("Helvetica", 11)
        for row in stats["by_student"][:20]:
            c.drawString(22 * mm, y, f"{row['student']}: {row['cnt']}")
            y -= 7 * mm
            if y < 20 * mm:
                c.showPage(); y = height - 20 * mm
                c.setFont(font_name, 11)

        c.showPage()
        c.save()
        buf.seek(0)

        resp = make_response(buf.read())
        resp.headers["Content-Type"] = "application/pdf"
        resp.headers["Content-Disposition"] = "attachment; filename=class_report.pdf"
        return resp
    except Exception as e:
        return make_response((f"生成PDF时出错: {str(e)}", 500))